package org.ycb.ycbisgod.ycbwoxihuanni.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.UnsupportedJwtException;
import jakarta.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.crypto.SecretKey;

@Component
public class JWTUtil {
    @Value("${jwt.secret}")
    private String secret;
    @Value("${jwt.expiration}")
    private long expiration;
    @Value("${jwt.token-header}")
    private String tokenHeader;
    @Value("${jwt.refresh-threshold}")
    private long refreshThreshold; // 这个值现在应该表示刷新阈值，通常以分钟为单位，用于判断过期后多久内可刷新。

    private SecretKey signingKey;

    @PostConstruct
    public void init(){
        this.signingKey=Keys.hmacShaKeyFor(secret.getBytes());
    }

    public String generateToken(Map<String,Object> claims){
        Date now = new Date();
        Date expirationDate = new Date(now.getTime() + expiration);
        return Jwts.builder()
                .claims(claims)
                .issuedAt(now)
                .expiration(expirationDate)
                .signWith(signingKey)
                .compact();
    }

    public Claims parseToken(String token){
        return Jwts.parser()
                .verifyWith(signingKey)
                .build()
                .parseSignedClaims(token)
                .getPayload();
    }

    public boolean validateToken(String token){
        try{
            parseToken(token); // 内部会验证过期时间
            return true;
        }
        catch(ExpiredJwtException e){
            System.out.println("Token已过期"+e.getMessage());
        }
        catch(SignatureException e){
            System.out.println("无效的Token"+e.getMessage());
        }
        catch(MalformedJwtException e){
            System.out.println("Token格式错误"+e.getMessage());
        }
        catch(UnsupportedJwtException e){
            System.out.println("Token不支持"+e.getMessage());
        }
        catch(IllegalArgumentException e){
            System.out.println("Token解析错误"+e.getMessage());
        }
        return false;
    }

    /**
     * 刷新 JWT Token
     *
     * @param token 旧的 JWT Token
     * @return 新的 JWT Token
     * @throws SecurityException 如果 Token 无法刷新（例如签名无效，或过期太久）
     */
    public String refreshToken(String token){
        Claims claims;
        long tokenExpirationMillis; // 旧 Token 的过期时间戳
        long nowMillis = System.currentTimeMillis();
        // 将配置的刷新阈值（分钟）转换为毫秒
        long refreshThresholdMillis = TimeUnit.MINUTES.toMillis(this.refreshThreshold);

        try{
            // 尝试正常解析 Token。如果 Token 尚未过期，则会进入这里。
            claims = parseToken(token);
            tokenExpirationMillis = claims.getExpiration().getTime();

            // 如果 Token 尚未过期，且距离过期时间还很远，则不需要刷新。
            // (tokenExpirationMillis - nowMillis) 计算的是 Token 还有多久过期。
            // 如果这个剩余时间大于刷新阈值，则直接返回原 Token。
            if(tokenExpirationMillis - nowMillis > refreshThresholdMillis){
                return token; // Token 仍然有效且离过期还远，无需刷新
            }
            // 如果到达这里，说明 Token 仍有效，但已接近过期（剩余时间小于等于 refreshThresholdMillis），可以进行预先刷新。
        }
        catch (ExpiredJwtException e ){
            // Token 已经过期。这是我们希望在刷新接口中处理的主要情况。
            claims = e.getClaims(); // 从 ExpiredJwtException 中获取 Claims (重要：这样才能拿到过期Token的有效载荷)
            tokenExpirationMillis = claims.getExpiration().getTime();

            // 检查 Token 是否在过期后仍在可刷新窗口内。
            // (nowMillis - tokenExpirationMillis) 计算的是 Token 已经过期了多久。
            // 如果这个过期时长超过了 refreshThresholdMillis，则认为 Token 彻底失效。
            if(nowMillis - tokenExpirationMillis > refreshThresholdMillis){
                throw new SecurityException("令牌过期，请重新登陆"); // Token 过期太久，需要重新登录
            }
            // 如果到达这里，说明 Token 已过期，但仍在可刷新窗口内。
        }
        // 捕获其他非过期引起的 JWT 异常，例如签名无效、格式错误等
        catch (io.jsonwebtoken.security.SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException e){
            throw new SecurityException("无效的令牌，无法刷新: " + e.getMessage());
        }
        // 捕获其他任何意料之外的异常
        catch (Exception e){
            throw new SecurityException("令牌刷新失败: " + e.getMessage());
        }

        // 创建新的 Claims，移除旧的签发和过期时间，让 generateToken 重新设置。
        Map<String,Object> newClaims = new HashMap<>(claims);
        newClaims.remove("iat"); // 移除旧的签发时间
        newClaims.remove("exp"); // 移除旧的过期时间

        return generateToken(newClaims); // 生成并返回新的 Token
    }
}